package nl.boektrust.btsoep.server.wsdl;

import java.util.Iterator;

import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SAAJResult;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.dom.DOMSource;

import nl.boektrust.btsoep.server.jaxws.DeletePerson;
import nl.boektrust.btsoep.server.jaxws.DeletePhysicalBookManifestation;
import nl.boektrust.btsoep.server.jaxws.GetAllPersons;
import nl.boektrust.btsoep.server.jaxws.GetAllPhysicalBookManifestations;
import nl.boektrust.btsoep.server.jaxws.GetBooksByISBN;
import nl.boektrust.btsoep.server.jaxws.GetBooksForPerson;
import nl.boektrust.btsoep.server.jaxws.GetNumberOfAssets;
import nl.boektrust.btsoep.server.jaxws.GetNumberOfEntities;
import nl.boektrust.btsoep.server.jaxws.GetNumberOfPhysicalBooks;
import nl.boektrust.btsoep.server.jaxws.GetPersonsByPartialName;
import nl.boektrust.btsoep.server.jaxws.PersistBTEntity;
import nl.boektrust.btsoep.server.jaxws.PersistPhysicalBookManifestation;
import nl.boektrust.btsoep.server.wsdl.jaxws.SearchBooks;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BrowseSoapHandler {
	private static final Logger logger = LoggerFactory.getLogger(BrowseSoapHandler.class);
	private final BrowseAdapter browseAdapter;
	private static final QName searchBooksQName = getQName(SearchBooks.class);
	private static final QName getBooksForPersonQName = getQName(GetBooksForPerson.class);
	private static final QName getAllPersonsQName = getQName(GetAllPersons.class);
	private static final QName persistPhysicalBookManifestationQName = getQName(PersistPhysicalBookManifestation.class);
	private static final QName persistBTEntityQName = getQName(PersistBTEntity.class);
	private static final QName getPersonsByPartialNameQName = getQName(GetPersonsByPartialName.class);
	private static final QName getNumberOfPhysicalBooksQName = getQName(GetNumberOfPhysicalBooks.class);
	private static final QName getNumberOfEntitiesQName = getQName(GetNumberOfEntities.class);
	private static final QName getNumberOfAssetsQName = getQName(GetNumberOfAssets.class);
	private static final QName getBooksByISBNQName = getQName(GetBooksByISBN.class);
	private static final QName getAllPhysicalBookManifestationsQName = getQName(GetAllPhysicalBookManifestations.class);
	private static final QName deletePhysicalBookManifestationQName = getQName(DeletePhysicalBookManifestation.class);
	private static final QName deletePersonQName = getQName(DeletePerson.class);

	public BrowseSoapHandler() {
		browseAdapter = new BrowseAdapter();
	}

	/**
	 * 
	 * @param clz
	 *            Generated by wsgen and annotated, for example
	 *            SearchBooks.class.
	 * @return QName with the namespace and element name. This makes it refactor
	 *         safe.
	 */
	public static QName getQName(Class<?> clz) {
		String name = clz.getAnnotation(XmlRootElement.class).name();
		String namespace = clz.getAnnotation(XmlRootElement.class).namespace();
		QName qn = new QName(namespace, name);
		// logger.debug("using QName=" + qn);
		return qn;
	}

	/**
	 * handleSOAPRequest.
	 * 
	 * @param request
	 *            Will be changed/reused to avoid calling
	 *            messageFactory.createMessage(). Input request of right QName
	 *            will be unmarshalled, send to the code behind the QName.
	 * @return The result of that code, and next marshalled.
	 * 
	 * @throws SOAPException
	 */
	public SOAPMessage handleSOAPRequest(SOAPMessage request) throws SOAPException {
		SOAPBody soapBody = request.getSOAPBody();
		if (soapBody == null)
			return makeFault("soapBody==null");
		Iterator<?> iterator = soapBody.getChildElements();
		while (iterator.hasNext()) {
			Object next = iterator.next();
			if (next instanceof SOAPElement) {
				SOAPElement soapElement = (SOAPElement) next;
				QName qname = soapElement.getElementQName();
				// chain of detection/dispatch, first one has shortened style,
				// choose your preference...
				if (getQName(SearchBooks.class).equals(qname)) {
					return helper(browseAdapter.searchBooks(JAXB.unmarshal(new DOMSource(soapElement), SearchBooks.class)));
				}
				if (getBooksForPersonQName.equals(qname)) {
					GetBooksForPerson getBooksForPerson = JAXB.unmarshal(new DOMSource(soapElement), GetBooksForPerson.class);
					return helper(browseAdapter.getBooksForPerson(getBooksForPerson));
				}
				if (getAllPersonsQName.equals(qname)) {
					GetAllPersons getAllPersons = JAXB.unmarshal(new DOMSource(soapElement), GetAllPersons.class);
					return helper(browseAdapter.getAllPersons(getAllPersons));
				}
				if (getAllPhysicalBookManifestationsQName.equals(qname)) {
					GetAllPhysicalBookManifestations getAllPhysicalBookManifestations = JAXB.unmarshal(new DOMSource(soapElement), GetAllPhysicalBookManifestations.class);
					return helper(browseAdapter.getAllPhysicalBookManifestations(getAllPhysicalBookManifestations));
				}
				if (persistPhysicalBookManifestationQName.equals(qname)) {
					PersistPhysicalBookManifestation persistPhysicalBookManifestation = JAXB.unmarshal(new DOMSource(soapElement), PersistPhysicalBookManifestation.class);
					return helper(browseAdapter.persistPhysicalBookManifestation(persistPhysicalBookManifestation));
				}
				if (persistBTEntityQName.equals(qname)) {
					PersistBTEntity persistBTEntity = JAXB.unmarshal(new DOMSource(soapElement), PersistBTEntity.class);
					return helper(browseAdapter.persistBTEntity(persistBTEntity));
				}
				if (getPersonsByPartialNameQName.equals(qname)) {
					GetPersonsByPartialName getPersonsByPartialName = JAXB.unmarshal(new DOMSource(soapElement), GetPersonsByPartialName.class);
					return helper(browseAdapter.getPersonsByPartialName(getPersonsByPartialName));
				}
				if (getNumberOfPhysicalBooksQName.equals(qname)) {
					GetNumberOfPhysicalBooks getNumberOfPhysicalBooks = JAXB.unmarshal(new DOMSource(soapElement), GetNumberOfPhysicalBooks.class);
					return helper(browseAdapter.getNumberOfPhysicalBooks(getNumberOfPhysicalBooks));
				}
				if (getNumberOfEntitiesQName.equals(qname)) {
					GetNumberOfEntities getNumberOfEntities = JAXB.unmarshal(new DOMSource(soapElement), GetNumberOfEntities.class);
					return helper(browseAdapter.getNumberOfEntities(getNumberOfEntities));
				}
				if (getNumberOfAssetsQName.equals(qname)) {
					GetNumberOfAssets getNumberOfAssets = JAXB.unmarshal(new DOMSource(soapElement), GetNumberOfAssets.class);
					return helper(browseAdapter.getNumberOfAssets(getNumberOfAssets));
				}
				if (getBooksByISBNQName.equals(qname)) {
					GetBooksByISBN getBooksByISBN = JAXB.unmarshal(new DOMSource(soapElement), GetBooksByISBN.class);
					return helper(browseAdapter.getBooksByISBN(getBooksByISBN));
				}
				if (deletePhysicalBookManifestationQName.equals(qname)) {
					DeletePhysicalBookManifestation deletePhysicalBookManifestation = JAXB.unmarshal(new DOMSource(soapElement), DeletePhysicalBookManifestation.class);
					return helper(browseAdapter.DeletePhysicalBookManifestation(deletePhysicalBookManifestation));
				}
				if (deletePersonQName.equals(qname)) {
					DeletePerson deletePerson = JAXB.unmarshal(new DOMSource(soapElement), DeletePerson.class);
					return helper(browseAdapter.deletePerson(deletePerson));
				}
				logger.debug("unhandled qname=" + qname);
			}
		}
		// if here then no handled request, send fault:
		return makeFault("Unrecognized SOAP request.");
	}

	public SOAPMessage makeFault(String err) throws SOAPException {
		SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
		SOAPBody soapBody = soapMessage.getSOAPBody();
		SOAPFault fault = soapBody.addFault();
		fault.setFaultString(err);
		return soapMessage;
	}

	private SOAPMessage helper(Object response) throws SOAPException {
		SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
		SOAPBody soapBody = soapMessage.getSOAPBody();
		JAXB.marshal(response, new SAAJResult(soapBody));
		return soapMessage;
	}

}
